﻿/*
* sqlGeometryUtils
* 
* initial implementation : 29.9.2008
*
* Copyright (c) Adam Sida, asida@centrum.cz.
* All rights reserved.
* 
* for license please ask for Petra.Gis.License.txt file
*/
using System;
using System.Data;
using System.Collections.Generic;
using System.Text;
using System.Data.SqlClient;
using System.Data.Sql;
using System.Data.SqlTypes;

using Microsoft.SqlServer.Server;

using Petra.Gis.DataServices;
using Petra.Gis.DataServices.GDO;
using Petra.Gis.DataServices.WKT;
using Petra.Gis.Geometry;

using Petra.Gis.Geometry.Utils;

namespace uhul.data
{
  public partial class sqlGeometryUtils
  {


    private static Petra.Gis.DataServices.GDO.GeometryFactory _ingrF;
    private static Petra.Gis.DataServices.WKB.GeometryFactory _ogcFB;
    private static Petra.Gis.DataServices.WKT.GeometryFactory _ogcFT;

    private static Petra.Gis.Geometry.Utils.CoordinateProcessor _clistMathRound;
    private static Petra.Gis.Geometry.Utils.CoordinateProcessor _clistMathTrim;

    static sqlGeometryUtils()
    { 
      _ingrF = new Petra.Gis.DataServices.GDO.GeometryFactory();
      _ogcFB = new Petra.Gis.DataServices.WKB.GeometryFactory();
      _ogcFT = new Petra.Gis.DataServices.WKT.GeometryFactory();

      _clistMathRound = new CoordinateProcessor();
      _clistMathRound.ProcessCoordinate = new Round();

      _clistMathTrim = new CoordinateProcessor();
      _clistMathTrim.ProcessCoordinate = new Trim();
    }




    /// <summary>
    /// returns string with name of geometry type
    /// for any geometry
    /// </summary>
    [SqlFunction(DataAccess = DataAccessKind.Read, IsDeterministic = true, IsPrecise = true)]
    public static string GetGeomType(SqlBytes gdoBlob)
    {
      if (gdoBlob.IsNull)
        return null;

      // Retrieving BLOB data
      //convert sqlbytes to byte[]
      byte[] blob = gdoBlob.Value;
      GeometryBase g = _ingrF.CreateGeometry(blob);

      string tmp = g.GetType().ToString();

      return tmp.Substring(tmp.LastIndexOf(".") + 1);
    }





    /// <summary>
    /// returns int size of current geometry blob in database
    /// </summary>
    [SqlFunction(DataAccess = DataAccessKind.Read, IsDeterministic = true, IsPrecise = true)]
    public static int GetGeomSize(SqlBytes gdoBlob)
    {
      if (gdoBlob.IsNull)
        return -1;

      // Retrieving BLOB data
      byte[] blobData = gdoBlob.Value;

      return blobData.Length;
    }






    /// <summary>
    /// returns byte[] formatted as gdo blob of geometry given by WKT string representation
    /// </summary>
    [SqlFunction(DataAccess = DataAccessKind.Read, IsDeterministic = true, IsPrecise = true)]
    public static SqlBytes Wkt2GdoBlob(string wktGeometry)
    {

      GeometryBase g = _ogcFT.CreateGeometry(ref wktGeometry);

      SqlBytes result = new SqlBytes((byte[])_ingrF.SaveGeometry(g));
      return result;

    }





    /// <summary>
    /// (fast - bulk function) returns byte[] formatted as gdo blob of geometry given by WKT string representation.
    /// WKT string must be optimized, without spaces, \n
    /// </summary>
    [SqlFunction(DataAccess = DataAccessKind.Read, IsDeterministic = true, IsPrecise = true)]
    public static SqlBytes Wkt2GdoBlobBulk(string wktGeometry)
    {

      GeometryBase g = _ogcFT.CreateGeometryBulk(ref wktGeometry);

      SqlBytes result = new SqlBytes((byte[])_ingrF.SaveGeometry(g));
      return result;

    }





    /// <summary>
    /// returns string representation of geometry blob (byte[])
    /// blob format must be gdoBlob
    /// </summary>
    [SqlFunction(DataAccess = DataAccessKind.Read, IsDeterministic = true, IsPrecise = true)]
    public static string GdoBlob2Wkt(SqlBytes gdoBlob)
    {

      //convert sqlbytes to byte[]
      byte[] blob = gdoBlob.Value;
      GeometryBase g = _ingrF.CreateGeometry(blob);

      if (_ogcFT.CanSave(g))
        return (string)_ogcFT.SaveGeometry(g);
      else
        return null;
    }





    /// <summary>
    /// returns envelope ordinate from wkt, text based geometry description
    /// </summary>
    /// <param name="wktGeometry">string, varchar, nvarchar with WKT formated geometry</param>
    /// <param name="sCoordName">name of coordinate, can be one of following (xlo, ylo, xhi, yhi)</param>
    /// <returns>double value of envelope edge</returns>
    [SqlFunction(DataAccess = DataAccessKind.Read, IsDeterministic = true, IsPrecise = true)]
    public static double GetWktEnvelopeCoord(string wktGeometry, string sCoordName)
    {
      double retVal = 0;
      Petra.Gis.DataServices.WKT.GeometryFactory ogcF = new Petra.Gis.DataServices.WKT.GeometryFactory();

      GeometryBase g = ogcF.CreateGeometry(wktGeometry);

      switch (sCoordName.ToLower())
      {
        case "xlo": retVal = g.BoundingBox.Left;
          break;
        case "ylo": retVal = g.BoundingBox.Bottom;
          break;
        case "xhi": retVal = g.BoundingBox.Right;
          break;
        case "yhi": retVal = g.BoundingBox.Top;
          break;
      }
      return retVal;
    }





    /// <summary>
    /// returns envelope ordinate from gdo blob geometry
    /// </summary>
    /// <param name="gdoBlob">byte[] or varbinary or binary geometry</param>
    /// <param name="sCoordName">name of coordinate, can be one of following (xlo, ylo, xhi, yhi)</param>
    /// <returns>double value of envelope edge</returns>
    [SqlFunction(DataAccess = DataAccessKind.Read, IsDeterministic = true, IsPrecise = true)]
    public static double GetGdoBlobEnvelopeCoord(SqlBytes gdoBlob, string sCoordName)
    {
        byte[] blobData = gdoBlob.Value;

        double retVal = 0;

        //convert sqlbytes to byte[]
        byte[] blob = gdoBlob.Value;
        GeometryBase g = _ingrF.CreateGeometry(blob);

        switch (sCoordName.ToLower())
        {
            case "xlo": retVal = g.BoundingBox.Left;
                break;
            case "ylo": retVal = g.BoundingBox.Bottom;
                break;
            case "xhi": retVal = g.BoundingBox.Right;
                break;
            case "yhi": retVal = g.BoundingBox.Top;
                break;
        }
        return retVal;
    }






    /// <summary>
    /// returns byte[] formatted as wkt blob of geometry given by gdo blob representation
    /// </summary>
    [SqlFunction(DataAccess = DataAccessKind.Read, IsDeterministic = true, IsPrecise = true)]
    public static SqlBytes GdoBlob2Wkb(SqlBytes gdoBlob)
    {

      // Retrieving BLOB data
      //convert sqlbytes to byte[]
      byte[] blob = gdoBlob.Value;
      GeometryBase g = _ingrF.CreateGeometry(blob);

      if (_ogcFB.CanSave(g))
        return new SqlBytes((byte[])_ogcFB.SaveGeometry(g));
      else
        return null;

    }




    /// <summary>
    /// returns byte[] formatted as gdo blob of geometry given by wkt blob representation
    /// </summary>
    [SqlFunction(DataAccess = DataAccessKind.Read, IsDeterministic = true, IsPrecise = true)]
    public static SqlBytes Wkb2GdoBlob(SqlBytes wkbBlob)
    {

      // Retrieving BLOB data
      //convert sqlbytes to byte[]
      byte[] blob = wkbBlob.Value;
      GeometryBase g = _ogcFB.CreateGeometry(blob);

      if (_ingrF.CanSave(g))
        return new SqlBytes((byte[])_ingrF.SaveGeometry(g));
      else
        return null;

    }





    /// <summary>
    /// returns bool value determines whether geometry blob is valid
    /// </summary>
    /// <param name="gdoBlob">geometry blob</param>
    /// <returns>true if null or valid geom, otherwise false</returns>
    [SqlFunction(DataAccess = DataAccessKind.Read, IsDeterministic = true, IsPrecise = true)]
    public static bool IsValid(SqlBytes gdoBlob)
    {
      if (gdoBlob.IsNull)
        return true;

      try
      {
        // Retrieving BLOB data
        //convert sqlbytes to byte[]
        byte[] blob = gdoBlob.Value;
        GeometryBase g = _ingrF.CreateGeometry(blob);
        g.GetType();
        return true;
      }
      catch 
      {
        return false;
      }
    }





    /// <summary>
    /// returns string with text string 
    /// that is stored inside GDO blob
    /// with Label geometry type
    /// </summary>
    [SqlFunction(DataAccess = DataAccessKind.Read, IsDeterministic = true, IsPrecise = true)]
    public static string GetGdoBlobText(SqlBytes gdoBlob)
    {
      if (gdoBlob.IsNull)
        return null;

      // Retrieving BLOB data
      //convert sqlbytes to byte[]
      byte[] blob = gdoBlob.Value;
      GeometryBase g = _ingrF.CreateGeometry(blob);

      if (!(g is Label)) //multilabel not supported
        return null;

      return ((Label)g).Text; ;
    }




    /// <summary>
    /// sets text of Label geometry
    /// returns byte[] formatted as gdo blob of geometry given by wkt blob representation
    /// </summary>
    [SqlFunction(DataAccess = DataAccessKind.Read, IsDeterministic = true, IsPrecise = true)]
    public static SqlBytes SetGdoBlobText(SqlBytes gdoBlob, string newText)
    {

      if (gdoBlob.IsNull || newText == null || newText == "")
        return null;

      // Retrieving BLOB data
      //convert sqlbytes to byte[]
      Label g = (Label)_ingrF.CreateGeometry(gdoBlob.Value); //multilabel not supported
      g.Text = newText;

      if (_ingrF.CanSave(g))
        return new SqlBytes((byte[])_ingrF.SaveGeometry(g));
      else
        return null;

    }



    [SqlFunction(DataAccess = DataAccessKind.Read, IsDeterministic = true, IsPrecise = true)]
    public static SqlBytes RoundGdoGeomCoords(SqlBytes gdoBlob, int decimals)
    {
      if (gdoBlob.IsNull)
        return null;

      byte[] blob = gdoBlob.Value;
      GeometryBase g = _ingrF.CreateGeometry(blob);

      ((Round)_clistMathRound.ProcessCoordinate).Decimals = (int)decimals;
      _clistMathRound.Transform(g);

      return new SqlBytes((byte[])_ingrF.SaveGeometry(g));

    }




    [SqlFunction(DataAccess = DataAccessKind.Read, IsDeterministic = true, IsPrecise = true)]
    public static SqlBytes TrimGdoGeomCoords(SqlBytes gdoBlob, int decimals)
    {
      if (gdoBlob.IsNull)
        return null;

      byte[] blob = gdoBlob.Value;
      GeometryBase g = _ingrF.CreateGeometry(blob);

      ((Trim)_clistMathTrim.ProcessCoordinate).Decimals = (int)decimals;
      _clistMathTrim.Transform(g);

      return new SqlBytes((byte[])_ingrF.SaveGeometry(g));

    }




    ///// <summary>
    ///// set prefered method for WKT geometry parsing.
    ///// </summary>
    ///// <param name="preferedTextParseMethod">
    ///// method name, currently HighPerformance or PreciseValidation values are supported
    ///// </param>
    //[SqlProcedure]
    //public static void PreferedWKTParser(string preferedTextParseMethod)
    //{

    //  switch (preferedTextParseMethod.ToLower())
    //  {
    //    case "highperformance":
    //      RegexGeometryParser.PreferedTextParser = RegexGeometryParser.PreferedRegexTypeEnum.HighPerformance;
    //      break;

    //    case "precisevalidation":
    //      RegexGeometryParser.PreferedTextParser = RegexGeometryParser.PreferedRegexTypeEnum.PreciseValidation;
    //      break;

    //  }

    //  //write output msg...
    //  SqlContext.Pipe.Send("PreferedTextParser is set to " + RegexGeometryParser.PreferedTextParser.ToString());
    //}



  }

}
